<template>
  <div
    class="field-validator el-form-item"
    :class="{
      'is-error': !validateState
    }"
  >
    <slot />
    <transition name="el-zoom-in-top">
      <div v-if="!validateState" class="el-form-item__error">
        {{ validateMessage }}
      </div>
    </transition>
  </div>
</template>

<script lang="ts" setup>
import _ from 'lodash'
import AsyncValidator from 'async-validator'
import { ObjTy } from '~/common'
import { $on,$off } from 'vue-happy-bus'

const props = defineProps({
  value: {
    type: [Array, Object, String, Number, Boolean, Date],
    default: null
  },
  tag: {
    type: [String, Number],
    default: ''
  },
  rules: {
    type: [Object, Array],
    default() {
      return []
    }
  }
})

const state = reactive({
  validateState: true,
  validateMessage: '',
  validateVal: null as any
})
const { validateState, validateMessage, validateVal } = toRefs(state)
const allRules = computed(() => {
  let rules: any[] = []
  if (Array.isArray(props.rules)) {
    rules = props.rules || []
  } else if (typeof props.rules === 'object') {
    rules = [props.rules]
  }
  return rules
})

const mappedRules = computed(() => {
  const groupedRules = _.groupBy(allRules, 'trigger')
  const mappedRules = _.pick(groupedRules, ['blur', 'change'])
  return mappedRules
})

watch(
  () => props.value,
  () => {
    state.validateVal = props.value
  }
)

onMounted(() => {
  registerValidateEvents()
  registerSelf()
})

onBeforeUnmount(() => {
  unregisterValidateEvents()
  unregisterSelf()
})

const validate = (trigger: string | number) => { 
  return new Promise((resolve) => {
    let rules: Array<ObjTy> = []
    if (trigger) {
      rules = mappedRules[trigger] || []
    } else {
      rules = allRules || []
    }

    if (!rules) {
      resolve(false)
    }
    if (rules.length === 0) {
      resolve(false)
    }

    rules.forEach((rule) => {
      delete rule.trigger
    })

    const model = { value: state.validateVal }
    const descriptor = { value: rules }

    const validator = new AsyncValidator(descriptor)

    validator.validate(model, { first: true, firstFields: true }, (errors, fields) => {
      const valid = !(errors && errors[0])
      state.validateState = valid
      state.validateMessage = valid ? '' : errors[0].message
      resolve(valid)
    })
  })
}
const onFieldBlur = (val: any) => {
  validate('blur')
}
const onFieldChange = (val: any) => {
  validate('change')
}
const registerValidateEvents = () => {
    $on('el.form.blur', onFieldBlur)
    $on('el.form.change', onFieldChange)
}
const unregisterValidateEvents = () => {
    $off()
}
const registerField = inject('registerField')
const unregisterField = inject('unregisterField')

const registerSelf = () => {
  registerField
}
const unregisterSelf = () => {
  unregisterField
}
</script>

<style lang="scss" scoped>
.field-validator {
  display: inline-block;
  padding: 0;
  margin: 0;
}
.el-form-item__error {
  position: relative !important;
}
</style>
